# 日志中间件 - 记录所有HTTP请求和响应
import time
import uuid
from typing import Callable

from fastapi import Request, Response
from starlette.middleware.base import BaseHTTPMiddleware

from config.logging_config import get_logger

logger = get_logger(__name__)


class LoggingMiddleware(BaseHTTPMiddleware):
    """HTTP请求日志中间件"""

    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        # 生成请求ID
        request_id = str(uuid.uuid4())[:8]

        # 记录请求开始时间
        start_time = time.time()

        # 记录请求信息
        logger.info(
            f"[{request_id}] 请求开始 - {request.method} {request.url} "
            f"- 客户端: {request.client.host if request.client else 'unknown'}"
        )

        # 记录请求体（如果是POST/PUT/PATCH请求）
        if request.method in ["POST", "PUT", "PATCH"]:
            try:
                body = await request.body()
                if body:
                    # 只记录前500个字符的请求体
                    body_str = body.decode('utf-8')[:500]
                    logger.debug(f"[{request_id}] 请求体: {body_str}")
            except Exception as e:
                logger.warning(f"[{request_id}] 无法读取请求体: {str(e)}")

        # 将请求ID添加到状态中，以便在异常处理中使用
        request.state.request_id = request_id

        try:
            # 处理请求
            response = await call_next(request)

            # 计算处理时间
            process_time = time.time() - start_time

            # 记录响应信息
            logger.info(
                f"[{request_id}] 请求完成 - 状态码: {response.status_code} "
                f"- 处理时间: {process_time:.3f}s"
            )

            # 添加响应头
            response.headers["X-Request-ID"] = request_id
            response.headers["X-Process-Time"] = str(process_time)

            return response

        except Exception as e:
            # 计算处理时间
            process_time = time.time() - start_time

            # 记录异常信息
            logger.error(
                f"[{request_id}] 请求异常 - 异常类型: {type(e).__name__} "
                f"- 异常信息: {str(e)} - 处理时间: {process_time:.3f}s",
                exc_info=True
            )

            # 重新抛出异常，让全局异常处理器处理
            raise e
